/* machine_pin.c */

#include "machine_pin.h"
#include "extmod/virtpin.h"

/*
 * Declerations.
 */

/* Local functions. */
STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

/* Class method, which would be called by class name. */
STATIC     void machine_pin_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
STATIC mp_obj_t machine_pin_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
       mp_obj_t machine_pin_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);


/* Instance methods, which would be called by class instance name. */
STATIC mp_obj_t machine_pin_high(mp_obj_t self_in);
STATIC mp_obj_t machine_pin_low(mp_obj_t self_in);
STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args);
STATIC mp_obj_t machine_pin_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);


static const GPIO_PinMode_Type machine_pin_modes[] =
{
    GPIO_PinMode_In_Analog    ,
    GPIO_PinMode_In_Floating  ,
    GPIO_PinMode_In_PullDown  ,
    GPIO_PinMode_In_PullUp    ,
    GPIO_PinMode_Out_OpenDrain,
    GPIO_PinMode_Out_PushPull ,
    GPIO_PinMode_AF_OpenDrain ,
    GPIO_PinMode_AF_PushPull  ,
};

/*
 * Functions.
 */

// Simplified mode setting used by the extmod modules
void machine_pin_set_mode(const machine_pin_obj_t *self, uint8_t mode)
{
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    gpio_init.Pins = (1u << self->gpio_pin);
    gpio_init.PinMode = machine_pin_modes[mode];
    GPIO_Init(self->gpio_port, &gpio_init);
}

/* pin.high() */
STATIC mp_obj_t machine_pin_high(mp_obj_t self_in)
{
    /* self_in is machine_pin_obj_t. */
    machine_pin_obj_t * pin = (machine_pin_obj_t *)self_in;
    GPIO_WriteBit(pin->gpio_port, 1u << pin->gpio_pin, 1u);

    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);

/* pin.low() */
STATIC mp_obj_t machine_pin_low(mp_obj_t self_in)
{
    /* self_in is machine_pin_obj_t. */
    machine_pin_obj_t * pin = (machine_pin_obj_t *)self_in;
    GPIO_WriteBit(pin->gpio_port, 1u << pin->gpio_pin, 0u);

    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);

/* pin.value([value]) */
STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args)
{
    /* args[0] is machine_pin_obj_t. */
    return machine_pin_obj_call(args[0], (n_args - 1), 0, args + 1);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);

/* pin.init(pin_id, ...) */
STATIC mp_obj_t machine_pin_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
    /* args[0] is machine_pin_obj_t. */
    return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_init);


typedef enum
{
    PIN_INIT_ARG_MODE = 0,
    PIN_INIT_ARG_VALUE,
    PIN_INIT_ARG_AF,
} machine_pin_init_arg_t;

STATIC mp_obj_t machine_pin_obj_init_helper (
    const machine_pin_obj_t *self, /* machine_pin_obj_t类型的变量，包含硬件信息 */
    size_t n_args, /* 位置参数数量 */
    const mp_obj_t *pos_args, /* 位置参数清单 */
    mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
    static const mp_arg_t allowed_args[] =
    {
        [PIN_INIT_ARG_MODE] { MP_QSTR_mode , MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = PIN_MODE_IN_PULLUP} },
        [PIN_INIT_ARG_VALUE]{ MP_QSTR_value, MP_ARG_KW_ONLY  | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        [PIN_INIT_ARG_AF]   { MP_QSTR_af   , MP_ARG_KW_ONLY  | MP_ARG_INT, {.u_int = 0}},
    };

    /* 解析参数 */
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    /* 配置硬件 */
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    gpio_init.Pins = (1u << self->gpio_pin);
    gpio_init.PinMode = machine_pin_modes[args[PIN_INIT_ARG_MODE].u_int];
    GPIO_Init(self->gpio_port, &gpio_init);

    if (args[PIN_INIT_ARG_MODE].u_int < PIN_MODE_AF_OPENDRAIN)
    {
        if (args[PIN_INIT_ARG_VALUE].u_obj != MP_OBJ_NULL)
        {
            if ( mp_obj_is_true(args[PIN_INIT_ARG_VALUE].u_obj) )
            {
                GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, 1u);
            }
            else
            {
                GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, 0u);
            }
        }
    }
    else
    {
        GPIO_PinAFConf(self->gpio_port, 1u << self->gpio_pin, (uint8_t)(args[PIN_INIT_ARG_AF].u_int));
    }

    return mp_const_none;
}

/* print(pin) */
STATIC     void machine_pin_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind)
{
    /* o is the machine_pin_obj_t. */
    (void)kind;
    const machine_pin_obj_t *self = MP_OBJ_TO_PTR(o);
    mp_printf(print, "Pin(%s)", qstr_str(self->name));
}

/* pin(val) */
STATIC mp_obj_t machine_pin_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args)
{
    /* self_in is machine_pin_obj_t. */
    mp_arg_check_num(n_args, n_kw, 0, 1, false);
    machine_pin_obj_t *self = self_in;

    if ( n_args == 0 )
    {
        //return MP_OBJ_NEW_SMALL_INT(mp_hal_pin_read(self));
        return MP_OBJ_NEW_SMALL_INT(GPIO_ReadInDataBit(self->gpio_port, 1u << self->gpio_pin) ? 1u: 0u);
    }
    else
    {
        //mp_hal_pin_write(self, mp_obj_is_true(args[0]));
        GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, mp_obj_is_true(args[0]) ? 1u : 0u);
        return mp_const_none;
    }
}

/* return an instance of machine_pin_obj_t. */
mp_obj_t machine_pin_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    const machine_pin_obj_t *pin = pin_find(args[0]);

    if ( (n_args > 1) || (n_kw > 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)pin;
}


/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] =
{
    /* Class instance methods. */
    { MP_ROM_QSTR(MP_QSTR_low),     MP_ROM_PTR(&machine_pin_low_obj) },
    { MP_ROM_QSTR(MP_QSTR_high),    MP_ROM_PTR(&machine_pin_high_obj) },
    { MP_ROM_QSTR(MP_QSTR_value),   MP_ROM_PTR(&machine_pin_value_obj) },
    { MP_ROM_QSTR(MP_QSTR_init),    MP_ROM_PTR(&machine_pin_init_obj) },

    /* Class constants. */
    { MP_ROM_QSTR(MP_QSTR_IN_ANALOG    ), MP_ROM_INT(PIN_MODE_IN_ANALOG    ) },
    { MP_ROM_QSTR(MP_QSTR_IN_FLOATING  ), MP_ROM_INT(PIN_MODE_IN_FLOATING  ) },
    { MP_ROM_QSTR(MP_QSTR_IN_PULLDOWN  ), MP_ROM_INT(PIN_MODE_IN_PULLDOWN  ) },
    { MP_ROM_QSTR(MP_QSTR_IN_PULLUP    ), MP_ROM_INT(PIN_MODE_IN_PULLUP    ) },
    { MP_ROM_QSTR(MP_QSTR_OUT_OPENDRAIN), MP_ROM_INT(PIN_MODE_OUT_OPENDRAIN) },
    { MP_ROM_QSTR(MP_QSTR_OUT_PUSHPULL ), MP_ROM_INT(PIN_MODE_OUT_PUSHPULL ) },
    { MP_ROM_QSTR(MP_QSTR_AF_OPENDRAIN ), MP_ROM_INT(PIN_MODE_AF_OPENDRAIN ) },
    { MP_ROM_QSTR(MP_QSTR_AF_PUSHPULL  ), MP_ROM_INT(PIN_MODE_AF_PUSHPULL  ) },

};
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);

/* to support virpin. */
STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
    (void)errcode;
    machine_pin_obj_t *self = self_in;

    switch (request)
    {
        case MP_PIN_READ:
        {
            /* read pin. */
            return GPIO_ReadInDataBit(self->gpio_port, 1u << self->gpio_pin) ? 1u: 0u;
        }
        case MP_PIN_WRITE:
        {
            /* write pin. */
            GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, arg);
            return 0;
        }
    }
    return -1;
}

STATIC const mp_pin_p_t pin_pin_p =
{
    .ioctl = pin_ioctl,
};

const mp_obj_type_t machine_pin_type =
{
    { &mp_type_type },
    .name        = MP_QSTR_Pin,
    .print       = machine_pin_obj_print, /* __repr__(), which would be called by print(<ClassName>). */
    .call        = machine_pin_obj_call,  /* __call__(), which can be called as <ClassName>(). */
    .make_new    = machine_pin_obj_make_new, /* create new class instance. */
    .protocol    = &pin_pin_p, /* to support virpin. */
    .locals_dict = (mp_obj_dict_t *)&machine_pin_locals_dict,
};


/*
 * User functions.
 */
/* 格式化pin对象，传入参数无论是已经初始化好的pin对象，还是一个表示pin清单中的索引编号，通过本函数都返回一个期望的pin对象 */
const machine_pin_obj_t *pin_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个Pin的实例，则直接送出这个pin */
    if ( mp_obj_is_type(user_obj, &machine_pin_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个代表Pin清单的索引，则通过索引在Pin清单中找到这个pin，然后送出这个pin */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t pin_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( pin_idx < machine_pin_board_pins_num)
        {
            return machine_pin_board_pins[pin_idx];
        }
    }

    /* 如果传入参数是一个字符串，则通过这个字符串在Pin清单中匹配引脚名字，然后送出找到的pin */
    const machine_pin_obj_t *named_pin_obj = pin_find_by_name(&machine_pin_board_pins_locals_dict, user_obj);
    if ( named_pin_obj )
    {
        return named_pin_obj;
    }

    mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't exist"));
}

/* 通过字符串在引脚清单中匹配引脚 */
const machine_pin_obj_t *pin_find_by_name(const mp_obj_dict_t *name_dict, mp_obj_t name)
{
    mp_map_t *name_map = mp_obj_dict_get_map((mp_obj_t)name_dict);
    mp_map_elem_t *name_elem = mp_map_lookup(name_map, name, MP_MAP_LOOKUP);

    if ( (name_elem != NULL) && (name_elem->value != NULL) )
    {
        return name_elem->value;
    }
    return NULL;
}

/* EOF. */

